using System;
using System.Globalization;
using System.Text;
using Oracle.ManagedDataAccess.Client;
using OracleInternal.Common;

namespace Oracle.ManagedDataAccess.Types
{
	internal static class DateTimeConv
	{
		internal static DateTime GetDateTime(byte[] byteRep, OracleDbType oraType, int offset = 0, int length = -1)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Entry, OracleTraceClassName.DateTimeConv, OracleTraceFuncName.GetDateTime);
			}
			try
			{
				if (oraType != OracleDbType.Date && oraType != OracleDbType.TimeStamp && oraType != OracleDbType.TimeStampLTZ && oraType != OracleDbType.TimeStampTZ)
				{
					throw new OracleTypeException(ResourceStringConstants.INT_ERR);
				}
				return ToDateTime(byteRep, oraType != OracleDbType.TimeStampTZ, offset, length);
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, OracleTraceTag.Error, OracleTraceClassName.DateTimeConv, OracleTraceFuncName.GetDateTime, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Exit, OracleTraceClassName.DateTimeConv, OracleTraceFuncName.GetDateTime);
				}
			}
		}

		internal static DateTime GetDateTime(byte[] byteRep, OraType oraType, int offset = 0, int length = -1)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Entry, OracleTraceClassName.DateTimeConv, OracleTraceFuncName.GetDateTime);
			}
			try
			{
				if (oraType != OraType.ORA_DATE && oraType != OraType.ORA_TIMESTAMP && oraType != OraType.ORA_TIMESTAMP_DTY && oraType != OraType.ORA_TIMESTAMP_LTZ && oraType != OraType.ORA_TIMESTAMP_LTZ_DTY && oraType != OraType.ORA_TIMESTAMP_TZ && oraType != OraType.ORA_TIMESTAMP_TZ_DTY)
				{
					throw new OracleTypeException(ResourceStringConstants.INT_ERR);
				}
				return ToDateTime(byteRep, oraType != OraType.ORA_TIMESTAMP_TZ && oraType != OraType.ORA_TIMESTAMP_TZ_DTY, offset, length);
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, OracleTraceTag.Error, OracleTraceClassName.DateTimeConv, OracleTraceFuncName.GetDateTime, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Exit, OracleTraceClassName.DateTimeConv, OracleTraceFuncName.GetDateTime);
				}
			}
		}

		internal static DateTime ToDateTime(byte[] byteRep, bool isNotTimeStampTZ, int offset, int length)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Entry, OracleTraceClassName.DateTimeConv, OracleTraceFuncName.ToDateTime);
			}
			try
			{
				if (length == -1 && byteRep != null)
				{
					length = byteRep.Length - offset;
				}
				int num = (byteRep[offset] - 100) * 100 + (byteRep[1 + offset] - 100);
				int num2 = byteRep[2 + offset] & 0xFF;
				int num3 = byteRep[3 + offset] & 0xFF;
				int num4 = (byteRep[4 + offset] & 0xFF) - 1;
				int num5 = (byteRep[5 + offset] & 0xFF) - 1;
				int second = (byteRep[6 + offset] & 0xFF) - 1;
				DateTime result;
				if (isNotTimeStampTZ)
				{
					int millisecond = 0;
					int num6 = 0;
					if (length > 7)
					{
						num6 = byteRep[7 + offset] << 24;
						num6 |= byteRep[8 + offset] << 16;
						num6 |= byteRep[9 + offset] << 8;
						num6 |= byteRep[10 + offset] & 0xFF;
						millisecond = num6 / 1000000;
						num6 %= 1000000;
					}
					result = new DateTime(num, num2, num3, num4, num5, second, millisecond);
					if (num6 > 0)
					{
						return result.AddTicks(num6 / 100);
					}
				}
				else
				{
					bool flag = true;
					DateTime? date = null;
					if (num >= 1800)
					{
						date = new DateTime(num, num2, num3, num4, num5, second);
					}
					TimeStamp.GetTimeZoneOffsetInfo(date, byteRep, out var hourTZ, out var minuteTZ, out var _, out var _, isDataInLocalTime: false, offset, length);
					int num7 = 1;
					if (num < 0)
					{
						num7 = -1;
					}
					if (hourTZ < 0 || minuteTZ < 0)
					{
						flag = false;
					}
					num5 += minuteTZ;
					num4 += hourTZ;
					if (!flag)
					{
						if (num5 < 0)
						{
							num5 += 60;
							num4--;
						}
						if (num4 < 0)
						{
							num4 += 24;
							num3--;
						}
						if (num3 < 1)
						{
							num3 += DateTime.DaysInMonth((num == 0) ? 4 : (num * num7), (num2 - 1 == 0) ? 12 : (num2 - 1));
							num2--;
						}
						if (num2 < 1)
						{
							num2 += 12;
							num--;
						}
						if (num < -4712)
						{
							throw new OverflowException();
						}
					}
					else
					{
						if (num5 > 59)
						{
							num5 -= 60;
							num4++;
						}
						if (num4 > 23)
						{
							num4 -= 24;
							num3++;
						}
						if (num3 > DateTime.DaysInMonth((num == 0) ? 4 : (num * num7), num2))
						{
							num3 -= DateTime.DaysInMonth((num == 0) ? 4 : (num * num7), num2);
							num2++;
						}
						if (num2 > 12)
						{
							num2 -= 12;
							num++;
						}
					}
					int millisecond2 = 0;
					int num8 = 0;
					if (length > 7)
					{
						num8 = byteRep[7 + offset] << 24;
						num8 |= byteRep[8 + offset] << 16;
						num8 |= byteRep[9 + offset] << 8;
						num8 |= byteRep[10 + offset] & 0xFF;
						millisecond2 = num8 / 1000000;
						num8 %= 1000000;
					}
					result = new DateTime(num, num2, num3, num4, num5, second, millisecond2);
					if (num8 > 0)
					{
						return result.AddTicks(num8 / 100);
					}
				}
				return result;
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, OracleTraceTag.Error, OracleTraceClassName.DateTimeConv, OracleTraceFuncName.ToDateTime, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Exit, OracleTraceClassName.DateTimeConv, OracleTraceFuncName.ToDateTime);
				}
			}
		}

		internal static string ToString(byte[] byteRep, int offset, int length, string format)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Entry, OracleTraceClassName.DateTimeConv, OracleTraceFuncName.ToString);
			}
			try
			{
				if (length == -1 && byteRep != null)
				{
					length = byteRep.Length - offset;
				}
				int year = (byteRep[offset] - 100) * 100 + (byteRep[1 + offset] - 100);
				int month = byteRep[2 + offset] & 0xFF;
				int day = byteRep[3 + offset] & 0xFF;
				int num = (byteRep[4 + offset] & 0xFF) - 1;
				int min = (byteRep[5 + offset] & 0xFF) - 1;
				int sec = (byteRep[6 + offset] & 0xFF) - 1;
				if (length > 7)
				{
					int num2 = (byteRep[7 + offset] << 24) | (byteRep[8 + offset] << 16) | (byteRep[9 + offset] << 8) | (byteRep[10 + offset] & 0xFF);
					_ = num2 / 1000000;
					_ = num2 % 1000000;
				}
				return nlsFormatToText(year, month, day, num, min, sec, -1, num < 12, null, format);
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, OracleTraceTag.Error, OracleTraceClassName.DateTimeConv, OracleTraceFuncName.ToString, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Exit, OracleTraceClassName.DateTimeConv, OracleTraceFuncName.ToString);
				}
			}
		}

		internal static string ToString(byte[] byteRep, bool bCopyData, TSType tsType, string format, int dataOffset = 0, int dataLength = -1, bool addTZOffset = true)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Entry, OracleTraceClassName.DateTimeConv, OracleTraceFuncName.ToString);
			}
			try
			{
				if (dataLength == -1)
				{
					dataLength = byteRep.Length;
				}
				if (tsType == TSType.LocalTimeTZ && dataLength != 13)
				{
					throw new ArgumentException();
				}
				byte[] array;
				if (bCopyData)
				{
					array = new byte[13];
					Array.Copy(byteRep, dataOffset, array, 0, dataLength);
				}
				else
				{
					array = byteRep;
				}
				int year;
				int month;
				int day;
				int hour;
				int minute;
				int second;
				int nanosecond;
				int hourTZ;
				int minuteTZ;
				int zoneID;
				if (tsType == TSType.LocalTimeTZ && !addTZOffset)
				{
					TimeStamp.ExtractTimeStampTZValuesFromByteArray(array, out year, out month, out day, out hour, out minute, out second, out nanosecond, out hourTZ, out minuteTZ, out zoneID);
				}
				else
				{
					TimeStamp.ExtractTimeStampValuesFromByteArray(array, out year, out month, out day, out hour, out minute, out second, out nanosecond, out hourTZ, out minuteTZ, out zoneID, tsType);
				}
				if (!TimeStamp.IsValidDateTime(year, month, day, hour, minute, second, nanosecond))
				{
					throw new OverflowException();
				}
				return nlsFormatToText(zone: (zoneID <= 0) ? OracleTimeStampTZ.ConstructTimeZone(hourTZ, minuteTZ) : TimeStamp.GetZoneName(zoneID), year: year, month: month, day: day, hour: hour, min: minute, sec: second, nanos: nanosecond, am: hour < 12, format: format, hoursTZ: hourTZ, minutesTZ: minuteTZ);
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, OracleTraceTag.Error, OracleTraceClassName.DateTimeConv, OracleTraceFuncName.ToString, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Exit, OracleTraceClassName.DateTimeConv, OracleTraceFuncName.ToString);
				}
			}
		}

		internal static string nlsFormatToText(int year, int month, int day, int hour, int min, int sec, int nanos, bool am, string zone, string format, int hoursTZ = -99, int minutesTZ = -99)
		{
			char[] array = (format + "      ").ToCharArray();
			int length = format.Length;
			StringBuilder stringBuilder = new StringBuilder(length + 25);
			DateTimeFormatInfo dateTimeFormatInfo = null;
			int num = ((year < 0) ? (Math.Abs(year) + 1) : year);
			bool flag = false;
			for (int i = 0; i < length; i++)
			{
				switch (array[i])
				{
				case 'R':
				case 'r':
					if (array[i + 1] != 'R' && array[i + 1] != 'r')
					{
						break;
					}
					if ((array[i + 2] == 'R' || array[i + 2] == 'r') && (array[i + 3] == 'R' || array[i + 3] == 'r'))
					{
						if (num < 1000)
						{
							stringBuilder.Append("0" + num);
						}
						else if (num < 100)
						{
							stringBuilder.Append("00" + num);
						}
						else if (num < 10)
						{
							stringBuilder.Append("000" + num);
						}
						else
						{
							stringBuilder.Append(num);
						}
						i += 3;
					}
					else
					{
						if (num >= 100)
						{
							num %= 100;
						}
						if (num < 10)
						{
							stringBuilder.Append("0" + num);
						}
						else
						{
							stringBuilder.Append(num);
						}
						i++;
					}
					break;
				case 'Y':
				case 'y':
					if (array[i + 1] != 'Y' && array[i + 1] != 'y')
					{
						break;
					}
					if ((array[i + 2] == 'Y' || array[i + 2] == 'y') && (array[i + 3] == 'Y' || array[i + 3] == 'y'))
					{
						int num2 = ((year < 0 && flag) ? Math.Abs(year) : num);
						if (num2 < 1000)
						{
							stringBuilder.Append("0" + num2);
						}
						else if (num2 < 100)
						{
							stringBuilder.Append("00" + num2);
						}
						else if (num2 < 10)
						{
							stringBuilder.Append("000" + num2);
						}
						else
						{
							stringBuilder.Append(num2);
						}
						i += 3;
					}
					else
					{
						if (num >= 100)
						{
							num %= 100;
						}
						if (num < 10)
						{
							stringBuilder.Append("0" + num);
						}
						else
						{
							stringBuilder.Append(num);
						}
						i++;
					}
					break;
				case 'D':
				case 'd':
					if (array[i + 1] == 'D' || array[i + 1] == 'd')
					{
						stringBuilder.Append(((day < 10) ? "0" : "") + day);
						i++;
					}
					break;
				case 'M':
				case 'm':
					if (array[i + 1] == 'M' || array[i + 1] == 'm')
					{
						stringBuilder.Append(((month < 10) ? "0" : "") + month);
						i++;
					}
					else if (array[i + 1] == 'I' || array[i + 1] == 'i')
					{
						stringBuilder.Append(((min < 10) ? "0" : "") + min);
						i++;
					}
					else
					{
						if ((array[i + 1] != 'O' && array[i + 1] != 'o') || (array[i + 2] != 'N' && array[i + 2] != 'n'))
						{
							break;
						}
						if ((array[i + 3] == 'T' || array[i + 3] == 't') && (array[i + 4] == 'H' || array[i + 4] == 'h'))
						{
							if (dateTimeFormatInfo == null)
							{
								dateTimeFormatInfo = DateTimeFormatInfo.CurrentInfo;
							}
							if (array[i] == 'm')
							{
								stringBuilder.Append(dateTimeFormatInfo.GetMonthName(month).ToLower());
							}
							else if (array[i + 1] == 'O')
							{
								stringBuilder.Append(dateTimeFormatInfo.GetMonthName(month).ToUpper());
							}
							else
							{
								stringBuilder.Append(dateTimeFormatInfo.GetMonthName(month));
							}
							i += 4;
						}
						else
						{
							if (dateTimeFormatInfo == null)
							{
								dateTimeFormatInfo = DateTimeFormatInfo.CurrentInfo;
							}
							if (array[i] == 'm')
							{
								stringBuilder.Append(dateTimeFormatInfo.GetAbbreviatedMonthName(month).ToLower());
							}
							else if (array[i + 1] == 'O')
							{
								stringBuilder.Append(dateTimeFormatInfo.GetAbbreviatedMonthName(month).ToUpper());
							}
							else
							{
								stringBuilder.Append(dateTimeFormatInfo.GetMonthName(month));
							}
							i += 2;
						}
					}
					break;
				case 'H':
				case 'h':
					if (array[i + 1] != 'H' && array[i + 1] != 'h')
					{
						break;
					}
					if (array[i + 2] == '2' || array[i + 3] == '4')
					{
						stringBuilder.Append(((hour < 10) ? "0" : "") + hour);
						i += 3;
						break;
					}
					if (hour > 12)
					{
						hour -= 12;
					}
					else if (hour == 0)
					{
						hour += 12;
					}
					stringBuilder.Append(((hour < 10) ? "0" : "") + hour);
					i++;
					break;
				case 'S':
				case 's':
					if (array[i + 1] == 'S' || array[i + 1] == 's')
					{
						stringBuilder.Append(((sec < 10) ? "0" : "") + sec);
						i++;
						if ((array[i + 1] == 'X' || array[i + 1] == 'x') && (array[i + 2] == 'F' || array[i + 2] == 'f') && (array[i + 3] == 'F' || array[i + 3] == 'f'))
						{
							stringBuilder.Append(".");
							i++;
						}
					}
					else if ((array[i + 1] == 'Y' || array[i + 1] == 'y') && (array[i + 2] == 'Y' || array[i + 2] == 'y') && (array[i + 3] == 'Y' || array[i + 3] == 'y') && year < 0)
					{
						stringBuilder.Append("-");
						flag = true;
					}
					break;
				case 'F':
				case 'f':
					if (array[i + 1] == 'F' || array[i + 1] == 'f')
					{
						string text = ((nanos <= 0) ? "000000000" : nanos.ToString("000000000"));
						if (char.IsDigit(array[i + 2]))
						{
							stringBuilder.Append(text.Substring(0, (int)char.GetNumericValue(array[i + 2])));
							i++;
						}
						else
						{
							stringBuilder.Append(text);
						}
						i++;
					}
					break;
				case 'T':
				case 't':
					if (array[i + 1] != 'Z' && array[i + 1] != 'z')
					{
						break;
					}
					if (array[i + 2] == 'R' || array[i + 2] == 'r')
					{
						if (zone.Length > 3 && zone.StartsWith("GMT"))
						{
							stringBuilder.Append(zone.Substring(3));
						}
						else
						{
							stringBuilder.Append(zone.ToUpper());
						}
						i += 2;
					}
					else if (array[i + 2] == 'H' || array[i + 2] == 'h')
					{
						if (zone != null && hoursTZ != -99)
						{
							if (hoursTZ >= 0)
							{
								stringBuilder.Append("+");
							}
							stringBuilder.Append(hoursTZ.ToString("00"));
						}
						i += 2;
					}
					else if (array[i + 2] == 'M' || array[i + 2] == 'm')
					{
						if (zone != null && minutesTZ != -99)
						{
							stringBuilder.Append(Math.Abs(minutesTZ).ToString("00"));
						}
						i += 2;
					}
					break;
				case 'A':
				case 'a':
					if (array[i + 1] == 'M' || array[i + 1] == 'm')
					{
						stringBuilder.Append(am ? "AM" : "PM");
						i++;
					}
					else if (array[i + 1] == 'D' || array[i + 1] == 'd')
					{
						stringBuilder.Append((year < 0) ? "BC" : "AD");
						i++;
					}
					break;
				case 'B':
				case 'b':
					if (array[i + 1] == 'C' || array[i + 1] == 'c')
					{
						stringBuilder.Append((year < 0) ? "BC" : "AD");
						i++;
					}
					break;
				case 'P':
				case 'p':
					if (array[i + 1] == 'M' || array[i + 1] == 'm')
					{
						stringBuilder.Append(am ? "AM" : "PM");
						i++;
					}
					break;
				default:
					stringBuilder.Append(array[i]);
					break;
				}
			}
			return stringBuilder.ToString(0, stringBuilder.Length);
		}
	}
}
